<!DOCTYPE html>
<html lang="en-AU">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Vue Testing Handbook (Vue.js 3)</title>
    <meta name="description" content="">
    <link rel="icon" href="https://lmiller1990.github.io/vue-testing-handbook/img/favicon.png">
  <meta property="og:title" content="Vue Testing Handbook">
  <meta property="og:description" content="Vue testing handbook">
  <meta property="og:type" content="website">
  <meta property="og:url" content="https://lmiller1990.github.io/vue-testing-handbook/">
  <meta property="og:image" content="https://lmiller1990.github.io/vue-testing-handbook/img/og.png">
    
    <link rel="preload" href="/vue-testing-handbook/assets/css/0.styles.99f2ca3b.css" as="style"><link rel="preload" href="/vue-testing-handbook/assets/js/app.14051834.js" as="script"><link rel="preload" href="/vue-testing-handbook/assets/js/3.119a321e.js" as="script"><link rel="preload" href="/vue-testing-handbook/assets/js/2.94957ecd.js" as="script"><link rel="preload" href="/vue-testing-handbook/assets/js/86.2ba55e96.js" as="script"><link rel="prefetch" href="/vue-testing-handbook/assets/js/10.7b1750c8.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/100.87c02eb0.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/101.9f86f8af.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/102.7cf5b3b5.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/103.9a447cc0.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/104.d960f455.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/105.42aa8c19.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/106.fa067e24.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/107.f6145328.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/108.4251a9d9.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/109.6e04b4aa.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/11.c9a15d65.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/110.203be49f.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/111.e66d4c1a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/112.d5cf2637.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/113.0e1df8ea.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/114.b1d0a5f5.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/115.d21ab6a2.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/116.e13d34ff.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/117.532bedf9.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/118.b99c8d4d.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/119.97666be8.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/12.8ec4d737.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/120.e5fbc47b.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/121.2d6cedde.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/122.7dc3c3fc.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/123.82fcb804.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/124.affd46d3.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/125.10d1ddcb.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/126.8ca1978a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/127.7b76e158.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/128.e0d8fdce.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/129.e861e49f.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/13.96ee585a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/130.474092c4.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/131.71e4509c.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/132.b48fe96d.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/133.9268758a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/134.6a042ef6.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/135.6df261f9.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/136.8aab4165.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/137.60d15690.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/138.29f827fd.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/139.e8db710e.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/14.522b77b9.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/140.8a3b2b26.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/141.cd5f96df.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/142.04a47072.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/143.ac0cb5e1.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/144.c69a73ac.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/15.2f00de2d.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/16.d120a376.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/17.9260e267.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/18.4e3a9d5e.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/19.bfe7cfa3.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/20.52828530.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/21.4d92035b.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/22.49a88bda.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/23.79de1f16.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/24.f45f108e.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/25.48371e8a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/26.bc7a5470.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/27.49bfeced.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/28.b697055e.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/29.0ef469da.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/30.bd209efe.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/31.ddf8aa10.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/32.95ec3267.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/33.52cf0ebc.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/34.73b1ca03.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/35.be79f171.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/36.52a4dcd2.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/37.a02e83c0.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/38.2a809ae1.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/39.31de0194.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/4.a86536d1.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/40.4a3cfcb7.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/41.db485265.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/42.8c4375ac.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/43.2f241bb5.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/44.43539973.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/45.619b76a6.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/46.00e17cc1.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/47.faf51fac.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/48.840de9e5.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/49.3410da74.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/5.d3ebdf21.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/50.647913c0.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/51.4990b428.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/52.85ff4209.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/53.7bbf915a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/54.1fc8316d.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/55.374436d3.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/56.40ebbbe6.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/57.40d9cc46.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/58.37ab0bcf.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/59.8df2efac.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/6.637082cf.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/60.326b4bdd.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/61.449fcfe0.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/62.f05ac3d6.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/63.e1723f7e.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/64.5b9e17ef.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/65.52e8ce24.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/66.cbdf653b.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/67.522e5bfd.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/68.1c27735b.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/69.1f5624e9.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/7.43089f9d.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/70.49fd6b50.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/71.20acc429.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/72.715f4620.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/73.ac123d7d.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/74.59a869d0.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/75.772485ad.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/76.b887e73a.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/77.70e54ee3.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/78.2306c3e7.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/79.e01a6cd1.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/8.4c4cdaa7.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/80.c12fc82f.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/81.44ef90b7.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/82.ff3143a2.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/83.22084435.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/84.485c66f4.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/85.d8afef97.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/87.d5642266.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/88.3ad1f8ac.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/89.e370d2fc.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/9.a0f31be6.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/90.2c039dfb.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/91.2c1dd586.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/92.571cd8ae.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/93.b88ae6f6.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/94.16000348.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/95.79a96428.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/96.c86f8cad.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/97.bc1d6c91.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/98.34c6a547.js"><link rel="prefetch" href="/vue-testing-handbook/assets/js/99.e1174558.js">
    <link rel="stylesheet" href="/vue-testing-handbook/assets/css/0.styles.99f2ca3b.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/vue-testing-handbook/v3/" class="home-link router-link-active"><!----> <span class="site-name">Vue Testing Handbook (Vue.js 3)</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="Select language" class="dropdown-title"><span class="title">Languages</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/reducing-boilerplate-in-tests.html" class="nav-link">English</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html" class="nav-link router-link-exact-active router-link-active">v3</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/ja/" class="nav-link">日本語</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/ru/reducing-boilerplate-in-tests.html" class="nav-link">Русский (Vue.js 2)</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/v3/ru/reducing-boilerplate-in-tests.html" class="nav-link">Русский (Vue.js 3)</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/zh-CN/" class="nav-link">简体中文</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/ko/reducing-boilerplate-in-tests.html" class="nav-link">한국어</a></li></ul></div></div> <a href="https://github.com/lmiller1990/vue-testing-handbook" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><a href="https://vuejs-course.com/" target="_blank"><img id="ad" src="https://raw.githubusercontent.com/lmiller1990/vue-testing-handbook/master/src/.vuepress/public/composition.png" alt="vuejs-course banner"></a> <br> <div class="info"><small>
    Hi! Get $10 off my
    <a href="https://vuejs-course.com/" target="_blank">new</a> <a href="https://vuejs-course.com/" target="_blank">course</a> <a href="https://vuejs-course.com/" target="_blank">on</a> <a href="https://vuejs-course.com/" target="_blank">Vue.js 3</a>,
    <a href="https://vuejs-course.com/" target="_blank">TypeScript and</a>,
    <a href="https://vuejs-course.com/" target="_blank">testing</a>,
    with the discount code VUEJS_COURSE_10_OFF.
    </small></div> <nav class="nav-links"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="Select language" class="dropdown-title"><span class="title">Languages</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/reducing-boilerplate-in-tests.html" class="nav-link">English</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html" class="nav-link router-link-exact-active router-link-active">v3</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/ja/" class="nav-link">日本語</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/ru/reducing-boilerplate-in-tests.html" class="nav-link">Русский (Vue.js 2)</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/v3/ru/reducing-boilerplate-in-tests.html" class="nav-link">Русский (Vue.js 3)</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/zh-CN/" class="nav-link">简体中文</a></li><li class="dropdown-item"><!----> <a href="/vue-testing-handbook/ko/reducing-boilerplate-in-tests.html" class="nav-link">한국어</a></li></ul></div></div> <a href="https://github.com/lmiller1990/vue-testing-handbook" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav>  <ul class="sidebar-links"><li><a href="/vue-testing-handbook/v3/" class="sidebar-link">Welcome</a></li><li><a href="/vue-testing-handbook/v3/setting-up-for-tdd.html" class="sidebar-link">Setting up for TDD</a></li><li><a href="/vue-testing-handbook/v3/rendering-a-component.html" class="sidebar-link">Rendering Components</a></li><li><a href="/vue-testing-handbook/v3/components-with-props.html" class="sidebar-link">Testing Props</a></li><li><a href="/vue-testing-handbook/v3/computed-properties.html" class="sidebar-link">Computed Properties</a></li><li><a href="/vue-testing-handbook/v3/simulating-user-input.html" class="sidebar-link">Simulating user input</a></li><li><a href="/vue-testing-handbook/v3/testing-emitted-events.html" class="sidebar-link">Testing emitted events</a></li><li><a href="/vue-testing-handbook/v3/mocking-global-objects.html" class="sidebar-link">Mocking global objects</a></li><li><a href="/vue-testing-handbook/v3/stubbing-components.html" class="sidebar-link">Stubbing components</a></li><li><a href="/vue-testing-handbook/v3/finding-elements-and-components.html" class="sidebar-link">Finding elements and components</a></li><li><a href="/vue-testing-handbook/v3/testing-vuex.html" class="sidebar-link">Testing Vuex</a></li><li><a href="/vue-testing-handbook/v3/vuex-mutations.html" class="sidebar-link">Vuex - Mutations</a></li><li><a href="/vue-testing-handbook/v3/vuex-actions.html" class="sidebar-link">Vuex - Actions</a></li><li><a href="/vue-testing-handbook/v3/vuex-getters.html" class="sidebar-link">Vuex - Getters</a></li><li><a href="/vue-testing-handbook/v3/vuex-in-components.html" class="sidebar-link">Vuex in components - $state and getters</a></li><li><a href="/vue-testing-handbook/v3/vuex-in-components-mutations-and-actions.html" class="sidebar-link">Vuex in components - mutations and actions</a></li><li><a href="/vue-testing-handbook/v3/vue-router.html" class="sidebar-link">Vue Router</a></li><li><a href="/vue-testing-handbook/v3/composition-api.html" class="sidebar-link">Composition API</a></li><li><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html" class="active sidebar-link">Reducing Boilerplate</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#reducing-boilerplate-in-tests" class="sidebar-link">Reducing Boilerplate in Tests</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#the-posts-component" class="sidebar-link">The Posts Component</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#vuex-vuerouter-factory-functions" class="sidebar-link">Vuex/VueRouter Factory Functions</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#the-tests-before-refactor" class="sidebar-link">The Tests (before refactor)</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#a-custom-createwrapper-function" class="sidebar-link">A Custom createWrapper Function</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#improving-the-createwrapper-function" class="sidebar-link">Improving the createWrapper function</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#setting-the-initial-vuex-state" class="sidebar-link">Setting the Initial Vuex State</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#improvements" class="sidebar-link">Improvements</a></li><li class="sidebar-sub-header"><a href="/vue-testing-handbook/v3/reducing-boilerplate-in-tests.html#conclusion" class="sidebar-link">Conclusion</a></li></ul></li><li><a href="/vue-testing-handbook/v3/jest-mocking-modules.html" class="sidebar-link">Jest - mocking modules</a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><div class="custom-block tip"><p class="custom-block-title">This book is written for Vue.js 3 and Vue Test Utils v2.</p> <p>Find the Vue.js 2 version <a href="/vue-testing-handbook/" class="router-link-active">here</a>.</p></div> <h2 id="reducing-boilerplate-in-tests"><a href="#reducing-boilerplate-in-tests" class="header-anchor">#</a> Reducing Boilerplate in Tests</h2> <blockquote><p>This article is available as a screencast on <a href="https://vuejs-course.com/screencasts/reducing-duplication-in-tests" target="_blank" rel="noopener noreferrer">Vue.js Courses<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>. Check it out <a href="https://vuejs-course.com/screencasts/reducing-duplication-in-tests" target="_blank" rel="noopener noreferrer">here<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p></blockquote> <p>It is often ideal to start each unit test with a fresh copy of a component. Furthermore, as your apps get larger and more complex, chances are you have a some components with many different props, and possibly a number of third party libraries such as Vuetify, VueRouter and Vuex installed. This can cause your tests to have lots of boilerplate code - that is, code that is not directly related to the test.</p> <p>This article takes component using Vuex and VueRouter and demonstrates some patterns to help you reduce the amount of setup code for your unit tests.</p> <p>The source code for the test described on this page can be found <a href="https://github.com/lmiller1990/vue-testing-handbook/tree/master/demo-app-vue-3/tests/unit/Posts.spec.js" target="_blank" rel="noopener noreferrer">here<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p> <h2 id="the-posts-component"><a href="#the-posts-component" class="header-anchor">#</a> The Posts Component</h2> <p>This is the component we will be testing. It shows a <code>message</code> prop, if one is received. It shows a New Post button if the user is authenticated and some posts. Both of the <code>authenticated</code> and <code>posts</code> objects come from the Vuex store. Finally, it renders are <code>router-link</code> component, showing a link to a post.</p> <div class="language-vue extra-class"><pre class="language-vue"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>message<span class="token punctuation">&quot;</span></span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>message<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>{{ message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>authenticated<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> 
        <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>new-post<span class="token punctuation">&quot;</span></span> 
        <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>/posts/new<span class="token punctuation">&quot;</span></span>
      <span class="token punctuation">&gt;</span></span>
        New Post
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">&gt;</span></span>Posts<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> 
      <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>post in posts<span class="token punctuation">&quot;</span></span> 
      <span class="token attr-name">:key</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>post.id<span class="token punctuation">&quot;</span></span> 
      <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>post<span class="token punctuation">&quot;</span></span>
    <span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span> <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>postLink(post.id)<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
        {{ post.title }}
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script"><span class="token language-javascript">
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token string">'Posts'</span><span class="token punctuation">,</span>
  props<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    message<span class="token punctuation">:</span> String<span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>

  computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token function">authenticated</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>authenticated
    <span class="token punctuation">}</span><span class="token punctuation">,</span>

    <span class="token function">posts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>posts
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>

  methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token function">postLink</span><span class="token punctuation">(</span><span class="token parameter">id</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/posts/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>We want to test:</p> <ul><li>is the <code>message</code> rendered when a prop is received?</li> <li>are the <code>posts</code> correctly rendered?</li> <li>is the New Post button shown when <code>authenticated</code> is <code>true</code>, hidden when <code>false</code>?</li></ul> <p>Ideally, the tests should be as concise as possible.</p> <h2 id="vuex-vuerouter-factory-functions"><a href="#vuex-vuerouter-factory-functions" class="header-anchor">#</a> Vuex/VueRouter Factory Functions</h2> <p>One good step you can take to making apps more testable is export factory functions for Vuex and VueRouter. Often, you will see something like:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// store.js</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token comment">// router.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>This is fine for a regular application, but not ideal for testing. If you do this, every time you use the store or router in a test, it will be shared across every other test that also imports it. Ideally, every component should get a fresh copy of the store and router.</p> <p>One easy way to work around this is by exporting a factory function - a function that returns a new instance of an object. For example:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// store.js</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">createVuexStore</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token comment">// router.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">createRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">createVueRouter</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">createRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Now your main app can do <code>import { store } from './store.js</code>, and your tests can get a new copy of the store each time by doing <code>import { createVuexStore } from './store.js</code>, then creating and instance with <code>const store = createStore()</code>. The same goes for the router. This is what I am doing in the <code>Posts.vue</code> example - the store code is found <a href="https://github.com/lmiller1990/vue-testing-handbook/tree/master/demo-app-vue-3/src/createStore.js" target="_blank" rel="noopener noreferrer">here<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a> and the router <a href="https://github.com/lmiller1990/vue-testing-handbook/tree/master/demo-app-vue-3/src/createRouter.js" target="_blank" rel="noopener noreferrer">here<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p> <h2 id="the-tests-before-refactor"><a href="#the-tests-before-refactor" class="header-anchor">#</a> The Tests (before refactor)</h2> <p>Now we know what <code>Posts.vue</code> and the store and router look like, we can understand what the tests are doing:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span> mount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span>

<span class="token keyword">import</span> Posts <span class="token keyword">from</span> <span class="token string">'@/components/Posts.vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> createVueRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/createRouter'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> createVuexStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/createStore'</span>

<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Posts.vue'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders a message if passed'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createVuexStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createVueRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token string">'New content coming soon!'</span>
    <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mount</span><span class="token punctuation">(</span>Posts<span class="token punctuation">,</span> <span class="token punctuation">{</span>
      global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      props<span class="token punctuation">:</span> <span class="token punctuation">{</span> message <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">&quot;#message&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token string">'New content coming soon!'</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders posts'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createVuexStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createVueRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token string">'New content coming soon!'</span>
    <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mount</span><span class="token punctuation">(</span>Posts<span class="token punctuation">,</span> <span class="token punctuation">{</span>
      global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      props<span class="token punctuation">:</span> <span class="token punctuation">{</span> message <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    wrapper<span class="token punctuation">.</span>vm<span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'ADD_POSTS'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">'Post'</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span>vm<span class="token punctuation">.</span><span class="token function">$nextTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token string">'.post'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>This does not fully tests all the conditions; it's a minimal example, and enough to get us started. Notice the duplication and repetition - let's get rid of that.</p> <h2 id="a-custom-createwrapper-function"><a href="#a-custom-createwrapper-function" class="header-anchor">#</a> A Custom <code>createWrapper</code> Function</h2> <p>The few lines of each test are the same:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createVuexStore</span><span class="token punctuation">(</span>storeState<span class="token punctuation">)</span>
<span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createVueRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">return</span> <span class="token function">mount</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> <span class="token punctuation">{</span>
  global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  props<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>Let's fix that with a function called <code>createWrapper</code>. It looks something like this:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">createWrapper</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span> store<span class="token punctuation">,</span> router <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Now we have encapsulated all the logic in a single function. We return the <code>store</code>, and <code>router</code> since we need to pass them to the <code>mount</code> function.</p> <p>If we refactor the first test using <code>createWrapper</code>, it looks like this:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders a message if passed'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> store<span class="token punctuation">,</span> router <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">createWrapper</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token string">'New content coming soon!'</span>
  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mount</span><span class="token punctuation">(</span>Posts<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
      plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    props<span class="token punctuation">:</span> <span class="token punctuation">{</span> message <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">&quot;#message&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token string">'New content coming soon!'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>Quite a bit more concise. Let's refactor second test, which makes use of the of Vuex store.</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders posts'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> store<span class="token punctuation">,</span> router <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">createWrapper</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mount</span><span class="token punctuation">(</span>Posts<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
      plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  wrapper<span class="token punctuation">.</span>vm<span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'ADD_POSTS'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">'Post'</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span>vm<span class="token punctuation">.</span><span class="token function">$nextTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token string">'.post'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><h2 id="improving-the-createwrapper-function"><a href="#improving-the-createwrapper-function" class="header-anchor">#</a> Improving the <code>createWrapper</code> function</h2> <p>While the above code is definitely an improvement, comparing this and the previous test, we can notice that about half of the code is still duplicated. Let's address this by updating the <code>createWrapper</code> function to handle mounting the component, too.</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">createWrapper</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">component<span class="token punctuation">,</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createVuexStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createVueRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">return</span> <span class="token function">mount</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
      plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token operator">...</span>options
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Now we can just called <code>createWrapper</code> and have a fresh copy of the component, ready for testing. Our tests are very concise now.</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders a message if passed'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token string">'New content coming soon!'</span>
  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">createWrapper</span><span class="token punctuation">(</span>Posts<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    props<span class="token punctuation">:</span> <span class="token punctuation">{</span> message <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">&quot;#message&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token string">'New content coming soon!'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders posts'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">createWrapper</span><span class="token punctuation">(</span>Posts<span class="token punctuation">)</span>

  wrapper<span class="token punctuation">.</span>vm<span class="token punctuation">.</span>$store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'ADD_POSTS'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">'Post'</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span>vm<span class="token punctuation">.</span><span class="token function">$nextTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token string">'.post'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><h2 id="setting-the-initial-vuex-state"><a href="#setting-the-initial-vuex-state" class="header-anchor">#</a> Setting the Initial Vuex State</h2> <p>The last improvement we can make is to how we populate the Vuex store. In a real application, you store is likely to be complex, and having to <code>commit</code> and <code>dispatch</code> many different mutations and actions to get your component into the state you want to test is not ideal. We can make a small change to our <code>createVuexStore</code> function, which makes it easier to set the initial state:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">createVuexStore</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">initialState <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function">state</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
        authenticated<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        posts<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token operator">...</span>initialState
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// ...</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>Now we can the desired initial state to the <code>createVuexStore</code> function via <code>createWrapper</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">createWrapper</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">component<span class="token punctuation">,</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> storeState <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createVuexStore</span><span class="token punctuation">(</span>storeState<span class="token punctuation">)</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">createVueRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">return</span> <span class="token function">mount</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
      plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>store<span class="token punctuation">,</span> router<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token operator">...</span>options
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Now our test now can be written as follows:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'renders posts'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">createWrapper</span><span class="token punctuation">(</span>Posts<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    posts<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">'Post'</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token string">'.post'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p>This is a big improvement! We went from a test where roughly half the code was boilerplate, and not actually related to the assertion, to two lines; one to prepare the component for testing, and one for the assertion.</p> <p>Another bonus of this refactor is we have a flexible <code>createWrapper</code> function, which we can use for all our tests.</p> <h2 id="improvements"><a href="#improvements" class="header-anchor">#</a> Improvements</h2> <p>There are some other potential improvements:</p> <ul><li>update the <code>createVuexStore</code> function to allow setting initial state for Vuex namespaced modules</li> <li>improve <code>createVueRouter</code> to set a specific route</li> <li>allow the user to pass a <code>shallow</code> or <code>mount</code> argument to <code>createWrapper</code></li></ul> <h2 id="conclusion"><a href="#conclusion" class="header-anchor">#</a> Conclusion</h2> <p>This guide discussed:</p> <ul><li>using factory functions to get a new instance of an object</li> <li>reducing boilerplate and duplication by extract common behavior</li></ul> <p>The source code for the test described on this page can be found <a href="https://github.com/lmiller1990/vue-testing-handbook/tree/master/demo-app-vue-3/tests/unit/Posts.spec.js" target="_blank" rel="noopener noreferrer">here<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>. It is also available as a screencast on <a href="https://vuejs-course.com/screencasts/reducing-duplication-in-tests" target="_blank" rel="noopener noreferrer">Vue.js Courses<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>.</p></div> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/lmiller1990/vue-testing-handbook/edit/master/v3/reducing-boilerplate-in-tests.md" target="_blank" rel="noopener noreferrer">Edit this page on GitHub</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div> <div class="last-updated"><span class="prefix">Last Updated:</span> <span class="time">13/09/2020, 5:46:08 pm</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/vue-testing-handbook/v3/composition-api.html" class="prev">Composition API</a></span> <span class="next"><a href="/vue-testing-handbook/v3/jest-mocking-modules.html">Jest - mocking modules</a>
      →
    </span></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/vue-testing-handbook/assets/js/app.14051834.js" defer></script><script src="/vue-testing-handbook/assets/js/3.119a321e.js" defer></script><script src="/vue-testing-handbook/assets/js/2.94957ecd.js" defer></script><script src="/vue-testing-handbook/assets/js/86.2ba55e96.js" defer></script>
  </body>
</html>
